<!-- **
    * @description select 选择器组件
** -->
<!-- **
    * @use 无需引入直接使用 <yfn-select v-model="value" :options="[]" @change="changeSelect"></yfn-select>
           eg: <yfn-select v-model="value" :options="[{label: '文本', value: 1}]" @change="changeSelect"></yfn-select>
           eg: <yfn-select v-model="value" :options="[{label: '选项组', children: [{label: '文本', value: 1}]}]" @change="changeSelect"></yfn-select>
           eg插槽: <template #option="{ props }"></template>
    * @params参数说明：
    * @params {v-model}                绑定值
    * @params {options}                选项数据 Array
    * @params {options-key}            选项数据键值对，自定义字段名 Object，eg：{label: 'title'}
    * @params {placeholder}            占位符，默认 "请选择"
    * @params {allow-clear}            是否允许清空，默认 true
    * @params {allow-search}           选项是否支持搜索，默认 true
    * @params {multiple}               是否多选，默认 false
    * @params {size}                   选择框大小，mini、small、medium、large
    * @params {loading}                选择框和下拉菜单显示加载中状态，默认 false
    * @params {...}                    继承 a-select 原生属性/事件
** -->
<!-- **
    * @params {options}                选项数据
    * @params {options.label}          文案
    * @params {options.value}          选项的 value
    * @params {options.children}       选项分组数据，参数同 options; eg: options = [{label: '分组1', children: [{label: '选项1', value: 1}]}]
    * @params {options.disabled}       是否禁用，默认 false
    * @params {options.visible}        选项是否显示，默认 true
    * @params {...}                    继承 a-option 原生属性
** -->
<!-- **
    * @events事件：
    * @change                          值改变时触发
    * @get-instance                    获取实例
    * @{...}                           继承 a-select 原生事件
** -->
<!-- **
    * @slot插槽：
    * @header                          自定义下拉菜单的页头
    * @footer                          自定义下拉菜单的页脚
    * @label                           自定义选择框展示内容, 参数 props
    * @option                          自定义选项内容, 参数 props
    * @{...}                           继承 a-select 原生插槽
** -->
<template>
  <a-select class="YfnSelect" ref="YfnSelect" v-if="biz.visible" v-bind="{ ...defaultOptions, ...attrs }">
    <!-- 选项 -->
    <template v-for="(item, i) in setOptions(props.options)" :key="i">
      <option-item v-if="item.visible" :options="item">
        <template #default="data">
          <slot name="option" :props="{ ...data }"></slot>
        </template>
      </option-item>
    </template>
    <!-- 插槽 -->
    <template v-for="list in defaultSlot" :key="list" #[list]>
      <slot :name="list" :props="{ ...$attrs }"></slot>
    </template>
    <template v-if="slots.label" #label="{ data }">
      <slot name="label" :props="{ ...data }"></slot>
    </template>
  </a-select>
</template>

<script lang="ts" setup>
  import { ref, reactive, defineProps, useAttrs, useSlots, h, defineAsyncComponent, getCurrentInstance } from 'vue'
  /* props */
  const props = defineProps({
    options: Array,
    optionsKey: Object,
  })
  /* proxy */
  const { proxy } = getCurrentInstance() as any
  // emit
  const emit = defineEmits(['get-instance'])
  /* attrs */
  const attrs = useAttrs()
  /* slots */
  const slots = useSlots()
  /* data */
  const defaultOptions: any = reactive({
    placeholder: '请选择',
    'allow-clear': true,
    'allow-search': true,
  })
  const biz: any = reactive({
    module: {},
    visible: false,
    popover: false,
    value: '',
  })
  const defaultSlot: any = ref([])
  /* fn */
  const getFile = () => {
    import(`@arco-design/web-vue/es`).then((res) => {
      biz.module = res
      biz.visible = true
      setInstance()
    })
  }
  const getKey = (key: any) => {
    const keyAttr = key.split('a-')[1]
    const keyArr = keyAttr.split('-')
    let keyR = ''
    keyArr.forEach((p: any) => {
      const str = `${p.charAt(0).toUpperCase()}${p.slice(1)}`
      keyR += str
    })
    return keyR
  }
  getFile()
  const setOptions = (arr: any = []) => {
    const options = arr.map((p: any) => {
      return { visible: true, ...keyTo(p, props.optionsKey) }
    })
    return options
  }
  // 实例
  const setInstance = () => {
    emit('get-instance', proxy)
  }
  // 键值转换
  const keyTo = (value: any = {}, keyValue: any = {}) => {
    let obj = { ...value }
    Object.keys(keyValue || {}).forEach((key) => {
      obj[key] ?? (obj[key] = obj[keyValue[key]])
    })
    return obj
  }
  // 插槽数据
  const getSlot = () => {
    const slotArr: any = ['header', 'prefix', 'trigger', 'search-icon', 'loading-icon', 'arrow-icon', 'footer', 'empty', 'option']
    defaultSlot.value = slotArr.filter((p: any) => {
      return !!slots[p]
    })
  }
  getSlot()
  /* render */
  const OptionItem = defineAsyncComponent(() => {
    return new Promise((resolve) => {
      const createEl: any = renderFn()
      resolve(createEl)
    })
  })
  const renderFn = () => {
    return {
      props: {
        options: Object,
      },
      render(): any {
        const createNode = (options: any) => {
          // 元素
          const childrenList = options.children
          const hasChildren = !!childrenList
          const elTag = hasChildren ? 'a-optgroup' : 'a-option'
          const elKey = getKey(elTag)
          const el = biz.module[elKey]
          // 属性
          let attrs: any = {}
          if (hasChildren) {
            attrs = {
              label: options.label,
            }
            options.options && (attrs.options = options)
          } else {
            attrs = options
          }
          // 子元素
          let childNodes: any = null
          if (hasChildren) {
            const childNodesArr = childrenList.map((p: any) => {
              return createNode(p)
            })
            childNodes = { default: () => childNodesArr }
          }
          if (elTag == 'a-option') {
            if (slots.option) {
              childNodes = { default: () => this.$slots.default(options) }
            } else {
              childNodes = { default: () => options.label || options.value }
            }
          }
          // 插槽
          return h(el, attrs, childNodes)
        }
        return createNode(this.options)
      },
    }
  }
</script>

<style scoped lang="less"></style>
